#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using UTable.Objects.Controls;
using System.Windows;
using System.Windows.Media;

namespace UTable.Objects
{
    public class UObjectProxy: IObject
    {
        private String _id;
        private UObject _content;
        private UObjectEntity _entity;

        internal UObjectProxy(UObjectEntity entity)
        {
            Entity = entity;
			_content = entity.Content;
            _id = entity.ID;
        }

        internal UObjectEntity Entity { 
            get
            {
                return _entity;
            }
            set
            {
                _entity = value;
                _entity.LayoutChanged += new ObjectLayoutChangedEventHandler(_entity_LayoutChanged);
                _entity.ObjectLoaded += new EventHandler(_entity_ObjectLoaded);
                _entity.Closed += new EventHandler(_entity_Closed);
                _entity.LayoutStateChanged += new EventHandler(_entity_LayoutStateChanged);
                _entity.OperatingStateChanged += new EventHandler(_entity_OperatingStateChanged);
                _entity.OwnerChanged += new EventHandler(_entity_OwnerChanged);
                _entity.GotFingerCapture += new UTable.Input.MultiTouch.FingerEventHandler(_entity_GotFingerCapture);
                _entity.LostFingerCapture += new UTable.Input.MultiTouch.FingerEventHandler(_entity_LostFingerCapture);
                _entity.InputReceived += new UTable.Input.InputEventHandler(_entity_InputReceived);
                _entity.ChildrenChanged += new ObjectChildrenChangedEventHandler(_entity_ChildrenChanged);
				_entity.DragStarted += new ObjectDragEventHandler(_entity_DragStarted);
				_entity.DragEnded += new ObjectDragEventHandler(_entity_DragEnded);
            }

        }

        void _entity_InputReceived(UTable.Input.InputEventArgs args)
        {
            if (InputReceived != null)
            {
                InputReceived.BeginInvoke(args, null, InputReceived.Target);
            }
            
/*
            if (_content == null)
                return;
            _content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent), 
                new object[] { UTable.Objects.Controls.UObject.ObjectEventType.InputReceived, new object[] {args} });*/

        }

        void _entity_LostFingerCapture(object sender, UTable.Input.MultiTouch.FingerEventArgs args)
        {
            if (LostFingerCapture != null)
            {
                LostFingerCapture.BeginInvoke(this, args, null, LostFingerCapture.Target);
            }

/*
            if (_content == null)
                return;
            _content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { UTable.Objects.Controls.UObject.ObjectEventType.LostFingerCapture, new object[] { sender, args } });*/

        }

        void _entity_GotFingerCapture(object sender, UTable.Input.MultiTouch.FingerEventArgs args)
        {
            if (GotFingerCapture != null)
            {
                GotFingerCapture.BeginInvoke(this, args, null, GotFingerCapture.Target);
            }
/*

            if (_content == null)
                return;
            _content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { UTable.Objects.Controls.UObject.ObjectEventType.GotFingerCapture, new object[] { sender, args } });*/

        }

        void _entity_OwnerChanged(object sender, EventArgs e)
        {
            if (OwnerChanged != null)
            {
                FrameworkElement content = OwnerChanged.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { ObjectEventType.OwnerChanged, new object[] { this, e } });
            }
        }

        void _entity_ChildrenChanged(IObject sender, ObjectChildrenChangedEventArgs args)
        {
            if (ChildrenChanged != null)
            {
                // recreate the args, so the properties are UObjectProxy instead of UObjectEntity
                IObject removed = null;
                IObject added = null;
                if (args.Added != null && args.Added is UObjectEntity)
                    added = new UObjectProxy(args.Added as UObjectEntity);
                if (args.Removed != null && args.Removed is UObjectEntity)
                    removed = new UObjectProxy(args.Removed as UObjectEntity);
                args = new ObjectChildrenChangedEventArgs(added, removed);

                FrameworkElement content = OwnerChanged.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                new UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { ObjectEventType.ChildrenChanged, new object[] { this, args } });
            }
        }

		void _entity_DragEnded(IObject sender, ObjectDragEventArgs args)
		{
			if (DragEnded != null)
			{
				FrameworkElement content = DragEnded.Target as FrameworkElement;
				if (content != null)
					content.Dispatcher.BeginInvoke(
				new UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
				new object[] { ObjectEventType.DragEnded, new object[] { this, args } });
			}
		}

		void _entity_DragStarted(IObject sender, ObjectDragEventArgs args)
		{
			if (DragStarted != null)
			{
				FrameworkElement content = DragStarted.Target as FrameworkElement;
				if (content != null)
					content.Dispatcher.BeginInvoke(
				new UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
				new object[] { ObjectEventType.DragStarted, new object[] { this, args } });
			}
		}

        void _entity_OperatingStateChanged(object sender, EventArgs e)
        {
            if (OperatingStateChanged != null)
            {
                FrameworkElement content = OperatingStateChanged.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { ObjectEventType.OperatingStateChanged, new object[] { this, e } });
            }
        }

        void _entity_LayoutStateChanged(object sender, EventArgs e)
        {
            if (LayoutStateChanged != null)
            {
                FrameworkElement content = LayoutStateChanged.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                                new object[] { ObjectEventType.LayoutStateChanged, new object[] { this, e } });
            }                
        }

        void _entity_Closed(object sender, EventArgs e)
        {
            if (Closed != null)
            {
                FrameworkElement content = Closed.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { ObjectEventType.Closed, new object[] { this, e } });
            }
        }

        void _entity_ObjectLoaded(object sender, EventArgs e)
        {
            if (ObjectLoaded != null)
            {
                FrameworkElement content = ObjectLoaded.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                new object[] { ObjectEventType.ObjectLoaded, new object[] { this, e } });
            }
        }

        void _entity_LayoutChanged(IObject sender, ObjectLayoutChangedEventArgs args)
        {
            if (LayoutChanged != null)
            {
                FrameworkElement content = LayoutChanged.Target as FrameworkElement;
                if (content != null)
                    content.Dispatcher.BeginInvoke(
                     new UTable.Objects.Controls.UObject.RaiseObjectEventHandler(this.RaiseObjectEvent),
                    new object[] { ObjectEventType.LayoutChanged, new object[] { this, args } });
            }
        }

        public UObject Content
        {
            get
            {
                return _content;
            }
            set
            {
                _content = value;
            }
        }

        private void SetProperty(UObjectEntity.PropertyType type, object value)
        {
                if (Entity != null)
                    Entity.Dispatcher.BeginInvoke(new UObjectEntity.SetPropertyHandler(Entity.SetProperty), new object[] { type, value , true});
		}

		private object GetProperty(UObjectEntity.PropertyType type)
        {
            if (Entity != null)
                return Entity.Dispatcher.Invoke(new UObjectEntity.GetPropertyHandler(Entity.GetProperty), new object[] { type });
            return null;
        }

        private object CallMethod(UObjectEntity.MethodType type, object[] param)
        {
            if (Entity != null)
                return Entity.Dispatcher.Invoke(
                    new UObjectEntity.CallMethodHandler(Entity.CallMethod), new object[] { type, param });
            return null;
        }

        private object CallUObjectMethod(UObjectEntity.MethodType type, object[] param)
        {
            if (_content != null)
                return _content.Dispatcher.Invoke(
                    new UObjectEntity.CallMethodHandler(_content.CallLocalMethod), new object[] { type, param });
            return null;
        }

        internal void RaiseObjectEvent(ObjectEventType type, object[] param)
        {
            switch (type)
            {
                case ObjectEventType.Closed:
                    if (Closed != null)
                        Closed(this, new EventArgs());
                    break;
                case ObjectEventType.LayoutChanged:
                    if (LayoutChanged != null)
                        LayoutChanged(this, param[1] as ObjectLayoutChangedEventArgs);
                    break;
                case ObjectEventType.ObjectLoaded:
                    if (ObjectLoaded != null)
                        ObjectLoaded(this, new EventArgs());
                    break;
                case ObjectEventType.LayoutStateChanged:
                    if (LayoutStateChanged != null)
                        LayoutStateChanged(this, new EventArgs());
                    break;
                case ObjectEventType.OperatingStateChanged:
                    if (OperatingStateChanged != null)
                        OperatingStateChanged(this, new EventArgs());
                    break;
                case ObjectEventType.OwnerChanged:
                    if (OwnerChanged != null)
                        OwnerChanged(this, new EventArgs());
                    break;
				case ObjectEventType.ChildrenChanged:
					if (ChildrenChanged != null)
						ChildrenChanged(this, param[1] as ObjectChildrenChangedEventArgs);
					break;
				case ObjectEventType.DragStarted:
					if (DragStarted != null)
						DragStarted(this, param[1] as ObjectDragEventArgs);
					break;
				case ObjectEventType.DragEnded:
					if (DragEnded != null)
						DragEnded(this, param[1] as ObjectDragEventArgs);
					break;
                case ObjectEventType.GotFingerCapture:
                    if (GotFingerCapture != null)
                        GotFingerCapture(this, (UTable.Input.MultiTouch.FingerEventArgs)param[1]);
                    break;
                case ObjectEventType.LostFingerCapture:
                    if (LostFingerCapture != null)
                        LostFingerCapture(this, (UTable.Input.MultiTouch.FingerEventArgs)param[1]);
                    break;
                case ObjectEventType.InputReceived:
                    if (InputReceived != null)
                        InputReceived(param[0] as UTable.Input.InputEventArgs);
                    break;
            }
        }

        #region IObject Members

        public double MinWidth
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.MinWidth); 
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.MinWidth, value);
            }
        }

        public double MinHeight
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.MinHeight);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.MinHeight, value);
            }
        }

        public double MaxWidth
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.MaxWidth);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.MaxWidth, value);
            }
        }

        public double MaxHeight
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.MaxHeight);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.MaxHeight, value);
            }
        }

        public Point Position
        {
            get
            {
                return (Point)GetProperty(UObjectEntity.PropertyType.Position);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Position, value);
            }
        }

        public double Orientation
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.Orientation);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Orientation, value);
            }
        }

        public Point RotateCenter
        {
            get
            {
                return (Point)GetProperty(UObjectEntity.PropertyType.RotateCenter);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.RotateCenter, value);
            }
        }

        public double ScaleX
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.ScaleX);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.ScaleX, value);
            }
        }

        public double ScaleY
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.ScaleY);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.ScaleY, value);
            }
        }

        public Point ScaleCenter
        {
            get
            {
                return (Point)GetProperty(UObjectEntity.PropertyType.ScaleCenter);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.ScaleCenter, value);
            }
        }

        public int ZIndex
        {
            get
            {
                return (int)GetProperty(UObjectEntity.PropertyType.ZIndex);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.ZIndex, value);
            }
        }

        public IObject Owner
        {
            get { return (IObject)GetProperty(UObjectEntity.PropertyType.Owner); }
            set { SetProperty(UObjectEntity.PropertyType.Owner, value); }
        }

        public ReadOnlyCollection<IObject> OwnedObjects
        {
            get { return (ReadOnlyCollection<IObject>)GetProperty(UObjectEntity.PropertyType.OwnedObjects); }
        }

        public ObjectOperatingState OperatingState
        {
            get
            {
                return (ObjectOperatingState)GetProperty(UObjectEntity.PropertyType.OperatingState);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.OperatingState, value);
            }
        }

        public ObjectLayoutState LayoutState
        {
            get
            {
                return (ObjectLayoutState)GetProperty(UObjectEntity.PropertyType.LayoutState);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.LayoutState, value);
            }
        }

        public bool CanHoldObjects
        {
            get
            {
                return (bool)GetProperty(UObjectEntity.PropertyType.CanHoldObjects);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.CanHoldObjects, value);
            }
        }


        public bool NotifyLayoutChangeEnabled
        {
            get
            {
                return (bool)GetProperty(UObjectEntity.PropertyType.NotifyLayoutChangeEnabled);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.NotifyLayoutChangeEnabled, value);
            }
        }


        public Point TranslatePoint(Point point, IObject relativeTo)
        {
            return (Point)CallMethod(UObjectEntity.MethodType.TranslatePoint, new object[] { point, relativeTo });
        }

        public void SendMessage(object message)
        {
            // CallMethod(UObjectEntity.MethodType.SendMessage, new object[] { message });
			_content.Dispatcher.BeginInvoke(new SendMessageHandler(_content.SendMessage), new object[] { message });
        }

		public void QueryData(object dataFlag, QueryDataCallback callback)
		{
			_content.Dispatcher.BeginInvoke(new QueryDataAsyncHandler(_content.QueryData), new object[] { dataFlag, callback });
		}

		public object QueryData(object dataFlag)
		{
			return _content.Dispatcher.Invoke(new QueryDataSyncHandler(_content.QueryData), new object[] { dataFlag });
		}

        public void Close()
        {
            // CallMethod(UObjectEntity.MethodType.Close, new object[] { });

            CallUObjectMethod(UObjectEntity.MethodType.Close, new object[] { });
        }

        public event ObjectLayoutChangedEventHandler LayoutChanged;

        public event EventHandler ObjectLoaded;

        public event EventHandler Closed;

        public event EventHandler LayoutStateChanged;

        public event EventHandler OperatingStateChanged;

        public event EventHandler OwnerChanged;

        public event ObjectChildrenChangedEventHandler ChildrenChanged;

		public event ObjectDragEventHandler DragStarted;

		public event ObjectDragEventHandler DragEnded;

        #endregion

        #region IVisualNode Members

        public string ID
        {
            get { return _id; }
        }

        public double Width
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.Width);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Width, value);
            }
        }

        public double Height
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.Height);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Height, value);
            }
        }
        public System.Windows.Media.Geometry Clip
        {
            get
            {
                Geometry clip = (System.Windows.Media.Geometry)GetProperty(UObjectEntity.PropertyType.Clip);
                if (clip == null)
                    return null;
                else
                    return clip.CloneCurrentValue();
            }
            set
            {
                Geometry clip = value.CloneCurrentValue();
                clip.Freeze();
                SetProperty(UObjectEntity.PropertyType.Clip, clip);
            }
        }

        internal Brush Background
        {
            get 
            {
                Brush background = (Brush)GetProperty(UObjectEntity.PropertyType.Background);
                if (background == null)
                    return null;
                else
                    return background.CloneCurrentValue();
            }
            set
            {
                Brush backgound = value.CloneCurrentValue();
                backgound.Freeze();
                SetProperty(UObjectEntity.PropertyType.Background, backgound);
            }
        }

        public Type ObjectType
        {
            get { return (Type)GetProperty(UObjectEntity.PropertyType.ObjectType); }
        }

        public Visibility Visibility
        {
            get
            {
                return (Visibility)GetProperty(UObjectEntity.PropertyType.Visibility);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Visibility, value);
            }
        }

        internal bool IsHitTestEnabled
        {
            get
            {
                return (bool)GetProperty(UObjectEntity.PropertyType.IsHitTestEnabled);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.IsHitTestEnabled, value);
            }
        }

        public new bool IsHitTestVisible
        {
            get
            {
                return (bool)GetProperty(UObjectEntity.PropertyType.IsHitTestVisible);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.IsHitTestVisible, value);
            }
        }

        public double Opacity
        {
            get
            {
                return (double)GetProperty(UObjectEntity.PropertyType.Opacity);
            }
            set
            {
                SetProperty(UObjectEntity.PropertyType.Opacity, value);
            }
        }

        #endregion

        #region IInputNode Members

        public void RaiseEvent(UTable.Input.InputEventArgs e)
        {
            CallMethod(UObjectEntity.MethodType.RaiseEvent, new object[] { e });
        }

        public void OnInputReceived(UTable.Input.InputEventArgs args)
        {
            CallMethod(UObjectEntity.MethodType.OnInputReceived, new object[] { args });
        }

        public bool CaptureFinger(int fingerId)
        {
            return (bool)CallMethod(UObjectEntity.MethodType.CaptureFinger, new object[] { fingerId });
        }

        public bool UncaptureFinger(int fingerId)
        {
            return (bool)CallMethod(UObjectEntity.MethodType.UncaptureFinger, new object[] { fingerId });
        }

        public event UTable.Input.MultiTouch.FingerEventHandler GotFingerCapture;

        public event UTable.Input.MultiTouch.FingerEventHandler LostFingerCapture;

        public event UTable.Input.InputEventHandler InputReceived;

        #endregion

    }
}
